home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / newsgrp / group93a.txt / 000003_icon-group-sender _Sun Jan 3 21:17:57 1993.msg < prev    next >
Internet Message Format  |  1993-04-21  |  34KB

  1. Received: by cheltenham.cs.arizona.edu; Mon, 4 Jan 1993 04:34:12 MST
  2. Date: 3 Jan 93 21:17:57 GMT
  3. From: agate!spool.mu.edu!olivea!pagesat!spssig.spss.com!uchinews!ellis!goer@ucbvax.Berkeley.EDU  (Richard L. Goerwitz)
  4. Organization: University of Chicago
  5. Subject: parser generator, part 1
  6. Message-Id: <1993Jan3.211757.28395@midway.uchicago.edu>
  7. Sender: icon-group-request@cs.arizona.edu
  8. To: icon-group@cs.arizona.edu
  9. Status: R
  10. Errors-To: icon-group-errors@cs.arizona.edu
  11.  
  12. Warning -
  13.  
  14. Don't bother unpacking this file unless a) you know what a parser
  15. generator is, and b) you are interested in thinking about how one
  16. should look for Icon.
  17.  
  18. -Richard Goerwitz
  19.  
  20. ---- Cut Here and feed the following to sh ----
  21. #!/bin/sh
  22. # This is a shell archive (produced by shar 3.49)
  23. # To extract the files from this archive, save it to a file, remove
  24. # everything above the "!/bin/sh" line above, and type "sh file_name".
  25. #
  26. # made 01/03/1993 20:21 UTC by richard@zenu
  27. # Source directory /u/richard/Ibpag
  28. #
  29. # existing files will NOT be overwritten unless -c is specified
  30. # This format requires very little intelligence at unshar time.
  31. # "if test", "cat", "rm", "echo", "true", and "sed" may be needed.
  32. #
  33. # This is part 1 of a multipart archive                                    
  34. # do not concatenate these parts, unpack them in order with /bin/sh        
  35. #
  36. # This shar contains:
  37. # length  mode       name
  38. # ------ ---------- ------------------------------------------
  39. #  41902 -rw-r--r-- ibpag.icn
  40. #  18563 -r--r--r-- maketbls.icn
  41. #   9627 -r--r--r-- preproc.icn
  42. #  25415 -r--r--r-- itokens.icn
  43. #   3663 -r--r--r-- debugme.icn
  44. #   1190 -r--r--r-- errors.icn
  45. #   2062 -r--r--r-- slashupto.icn
  46. #   4314 -r--r--r-- rewrap.icn
  47. #    762 -r--r--r-- strip.icn
  48. #   1752 -rw-r--r-- Makefile.dist
  49. #
  50. if test -r _shar_seq_.tmp; then
  51.     echo 'Must unpack archives in sequence!'
  52.     echo Please unpack part `cat _shar_seq_.tmp` next
  53.     exit 1
  54. fi
  55. # ============= ibpag.icn ==============
  56. if test -f 'ibpag.icn' -a X"$1" != X"-c"; then
  57.     echo 'x - skipping ibpag.icn (File already exists)'
  58.     rm -f _shar_wnt_.tmp
  59. else
  60. > _shar_wnt_.tmp
  61. echo 'x - extracting ibpag.icn (Text)'
  62. sed 's/^X//' << 'SHAR_EOF' > 'ibpag.icn' &&
  63. X############################################################################
  64. X#
  65. X#    Name:     ibpag.src
  66. X#
  67. X#    Title:     Icon-based parser generator
  68. X#
  69. X#    Author:     Richard L. Goerwitz
  70. X#
  71. X#    Version: 1.21
  72. X#
  73. X############################################################################
  74. X#
  75. X#
  76. X#  General Description:
  77. X#
  78. X#      IBPAG is a simple tool for generating parsers from grammar
  79. X#  specifications.  Sounds pretty mundane, but those who have never
  80. X#  used a parser generator will perhaps be surprised to find that this
  81. X#  kind of tool forms the basis of most modern programming language
  82. X#  implementations.  Parser generators are also used in preprocessors,
  83. X#  transducers, compilers, interpreters, calculators and in fact for
  84. X#  just about any situation where some form of structured input needs
  85. X#  to be converted into some form of structured output.
  86. X#
  87. X#      IBPAG is not a full, working system.  In particular, startup
  88. X#  times for compiled IBPAG programs is very slow, since I can't find
  89. X#  a good way of writing/reconstructing the necessary structures.  For
  90. X#  applications where a startup delay is not a problem, IBPAG will
  91. X#  work fine.  What IBPAG is supposed to be is a very preliminary
  92. X#  crack at designing a parser generator for Icon.  It really ought to
  93. X#  be redone in C some day, and its error handling facilities need to
  94. X#  be tested better.  It also ought to be redone as an LALR (and not
  95. X#  LR) parser, and some method of storing parse tables ought to be
  96. X#  found that permits fast/easy reconstruction at run-time.  This
  97. X#  version has to be regarded as in beta testing.  I doubt I'll ever
  98. X#  take it, however, much further - at least in its present form.
  99. X#
  100. X#
  101. X#  More technical description:
  102. X#
  103. X#      Technically speaking, IBPAG is a preprocessor that accepts a
  104. X#  Icon-like source file from the standard input containing grammar
  105. X#  productions and actions, converts them into parsing tables and
  106. X#  associated code, and then adds to this an LR parser, tokenizer,
  107. X#  error handler, and a few debugging tools.  Writes the combination
  108. X#  to the standard output, along with the necessary action and goto
  109. X#  tables.  The tables are generated using a very general (but also
  110. X#  very sluggish) LR(1) algorithm.  IBPAG is not terribly practical
  111. X#  for most micros.  It was written as an excercize by me (a linguist
  112. X#  with far too little CS training) while auditing a compiler course
  113. X#  as a some-time diversion from many dissertation rewrites...
  114. X#
  115. X#      Cycles and epsilon moves are handled correctly (to my
  116. X#  knowledge).  Shift-reduce conflicts are handled in the normal way
  117. X#  (i.e. pick the rule with the highest priority, and, in cases where
  118. X#  the priority is the same, check the associativities; flag
  119. X#  reduce/reduce conflicts as errors, and for shift/reduce conflicts
  120. X#  pick shift by default [shift/shift conflicts don't matter, since we
  121. X#  shift in either case, and no reduction takes place]).
  122. X#
  123. X#
  124. X#  Running IBPAG:
  125. X#
  126. X#      Invoking IBPAG is very, very simple.  There are just two
  127. X#  (optional) command-line switches:
  128. X#
  129. X#      ibpag [-d] [-v] < inputfile > outputfile
  130. X#
  131. X#  Where the -d switch causes profuse debugging messages to be
  132. X#  displayed, and the -v switch ("verbose") causes somewhat more
  133. X#  restrained messages to be emitted.  -d implies -v.  Inputfile is an
  134. X#  IBPAG source file (see below on its format).  Outputfile is an LR
  135. X#  parser with appropriate tables, generated from the specs contained
  136. X#  in inputfile.
  137. X#
  138. X#      For more information how to set up IBPAG files and incorporate
  139. X#  them into larger Icon programs, see the section just below, "How to
  140. X#  use IBPAG."
  141. X#
  142. X############################################################################
  143. X#
  144. X#
  145. X#  How to use IBPAG
  146. X#  ________________
  147. X#
  148. X#
  149. X#  I. Prerequisites
  150. X#
  151. X#      The only prerequisites to using IBPAG are a knowledge of how to
  152. X#  write a basic LR-parsable context-free grammar and a familiarity
  153. X#  with the Icon programming language.  IBPAG source files use
  154. X#  essentially the same syntax as Icon source files.  In a couple of
  155. X#  cases, though the semantics of are quite different.  IBPAG is
  156. X#  essentially just a preprocessor.  IBPAG-specific constructs,
  157. X#  therefore, should be seen, not as Icon expressions, but as
  158. X#  directives to the preprocessor, specifying how to construct the
  159. X#  actual output code.
  160. X#
  161. X#      In the sections that follow, I will discuss how to set up an
  162. X#  IBPAG file, i.e. how to declare a start symbol and write rules.
  163. X#  The issue of the tokenizer will then be addressed, along with a
  164. X#  treatment of epsilon, and two reserved tokens:  $ and error.
  165. X#  Finally the overall compatibility of IBPAG and Icon code will be
  166. X#  discussed, as well as how to start the IBPAG parser from the Icon
  167. X#  code.  If you still have problems and/or questions, try reading the
  168. X#  UNIX manual page on YACC.  IBPAG and YACC have some superficial
  169. X#  similarities that may be helpful in sorting out why IBPAG works the
  170. X#  way it does.  Alternatively write to me, Richard Goerwitz, via
  171. X#  e-mail at goer@midway.uchicago.edu.  I'll be glad to field
  172. X#  questions and provide anyone with the latest version I have
  173. X#  on-hand.  Doubtless there will be many bugs to find, and I'll be
  174. X#  happy to fix any that get reported.  At some point, the entire
  175. X#  table generating process (in maketbls.icn) will need to be ripped
  176. X#  out, and replaced with one of the new, faster LR(1) (or perhaps
  177. X#  LALR) algorithms.
  178. X#
  179. X#
  180. X#  II. The start symbol
  181. X#
  182. X#      All context-free grammars have a start-symbol, i.e. a terminal
  183. X#  that can be considered the "goal" of a parse.  Because cycles (and
  184. X#  bugs) in the grammar can make it hard for IBPAG to determine what
  185. X#  the start symbol is, it must be declared.  This is done with a
  186. X#  start-symbol declaration, which has the following form:
  187. X#
  188. X#      <start-symbol-declaration> ::= start_symbol identifier
  189. X#
  190. X#  If no start-symbol declaration appears in an IBPAG source file,
  191. X#  IBPAG assumes S as the start symbol.
  192. X#
  193. X#
  194. X#  III. Rule declarations
  195. X#
  196. X#      The heart of the IBPAG source file is the rule declaration.
  197. X#  Basically, rule declarations tell IBPAG the structure of the grammar
  198. X#  it is to assume when generating a parser.  They also tell it what
  199. X#  rules to associate with what Icon code.  Rule declarations look a
  200. X#  lot like procedure declarations, except that they begin with "rule"
  201. X#  instead of "procedure" and must specify a priority and
  202. X#  associativity.  For those with formalistic leanings, the format of
  203. X#  the rule declaration is as follows (opt = optional):
  204. X#
  205. X#      <rule-declaration>  ::= <rule-header> ... end
  206. X#      <rule-header>       ::= rule <priority> <associativity> \
  207. X#                   <left-hand-symbol> ( <right-hand-symbol-list> )
  208. X#      <priority>          ::= integer-literal (opt)
  209. X#      <associativity>     ::= left | right | none (opt)
  210. X#      <left-hand-side>    ::= identifier
  211. X#      <right-hand-side>   ::= <symbol-spec> | <symbol-spec>,<right-hand-side>
  212. X#      <symbol-spec>       ::= <symbol> | <symbol> \| <symbol-spec>
  213. X#      <symbol>            ::= identifier | string-literal
  214. X#
  215. X#  The "..." indicates material that is precisely the same as for a
  216. X#  standard Icon procedure declaration.  The only difference between a
  217. X#  rule and a procedure (syntactically, that is) is in the header.
  218. X#  Superficially, rule and procedure headers look a lot alike.  The
  219. X#  rule headers, though, have extra elements (optional priority and
  220. X#  associativity).  They also interpret arguments in the option list
  221. X#  quite differently than Icon does.  In particular, these arguments
  222. X#  are either strings, identifiers, or any combination of these
  223. X#  separated by a bar.  Strings correspond to terminals, identifiers
  224. X#  to nonterminals.  The bar will be discussed below.
  225. X#
  226. X#      For those with a more practical bent, here is a sample rule
  227. X#  definition:
  228. X#
  229. X#      rule 1 none S(NP, VP)
  230. X#          return ["S", arg1, arg2]
  231. X#      end
  232. X#
  233. X#  The 1 denotes the priority (here very low), while "none" specifies
  234. X#  the associativity (can be right, left, or none).  S is the
  235. X#  left-hand side of the rule, and NP and VP are the (nonterminal)
  236. X#  symbols that make up its right-hand side.  Normally, the priority
  237. X#  is not needed (the default is 1).  In fact, it is best to try to
  238. X#  write grammars without any priorities or associativity rules.  The
  239. X#  default associativity is none.
  240. X#
  241. X#      Please note the body of the rule itself.  The code there is
  242. X#  triggered whenever an S is found (i.e. when a reduction via the
  243. X#  above S-rule occurs during the parse).  For those who know YACC,
  244. X#  arg1 is equivalent to $1 and the return expression is equivalent to
  245. X#  $$ =.  Essentially, the above rule definition says that an S is
  246. X#  composed of an NP and a VP, and that if an S is encountered, the
  247. X#  parser is to create a list having three members, the first being
  248. X#  the string "S", the second being the result that was returned when
  249. X#  the NP was found, and the third being the result returned when the
  250. X#  VP was encountered.  Note that the above rule implies the existence
  251. X#  of at least two other rules having NP and VP, respectively, as
  252. X#  their left-hand sides.  For example:
  253. X#
  254. X#      rule NP("det", "noun")
  255. X#          return ["NP", arg1, arg2]
  256. X#      end
  257. X#
  258. X#      rule VP("v", NP)
  259. X#          return ["VP", arg1, arg2]
  260. X#      end
  261. X#
  262. X#      If two or more right-hand sides differ in just a single symbol,
  263. X#  it is acceptable to combine them into one declaration using a
  264. X#  vertical slash or bar.  Although this bar looks like the usual Icon
  265. X#  alternation operator, in this context IBPAG uses it as a macro
  266. X#  device.  In the following case, two rules are generated, each with
  267. X#  one of the alternatives, "that" or "which," in its RHS.  The same
  268. X#  code is triggered by a reduction via either rule:
  269. X#
  270. X#      rule RELCL("that"|"which", S)
  271. X#          return ["RELCL", arg1, arg2]
  272. X#      end
  273. X#
  274. X#
  275. X#  IV. The tokenizer
  276. X#
  277. X#      Note that in the above examples, "v," "det," "noun," "that", and
  278. X#  "which" are considered terminal symbols.  The user must, in fact,
  279. X#  create a lexical analyzer called iparse_tokens that returns these
  280. X#  symbols.  The precise form in which iparse_tokens must function is
  281. X#  as follows:
  282. X#
  283. X#      iparse_tokens:  file     -> TOK records
  284. X#                      (stream) -> Ts
  285. X#      Where stream is an open file, and Ts are TOK records.
  286. X#      As a side-effect, iparse_tokens should increment the global
  287. X#      variable line_number (once right away, then once for each
  288. X#      newline encountered, or anything equivalent thereto)
  289. X#
  290. X#  The -> notation above implies that iparse_tokens is a pure
  291. X#  function.  I just can't think of a good notation for generators.
  292. X#  Iparse_tokens in fact *must be* a generator.  TOK records contain
  293. X#  the terminal symbol names in their first field.  If the literal
  294. X#  string value of the symbols is to be used by any of the rule code,
  295. X#  it should be stored in the second field of the TOK record.  A
  296. X#  typical value returned might be:
  297. X#
  298. X#      TOK("v", "eat")
  299. X#
  300. X#  YACC/LEX's heterogenous yylval/return (TOKEN) strategy is really
  301. X#  quite inelegant, and although my system isn't perfect, it avoids
  302. X#  the global variable, and hence makes running simultaneous parsers a
  303. X#  bit simpler undertaking (the only globals to worry about in IBPAG
  304. X#  are line_number, errors, and fail_on_error, all of which can, if
  305. X#  desired, be ignored).
  306. X#
  307. X#      If you want line numbers to be output with any error messages
  308. X#  that the parser might report, then make sure that the tokenizer
  309. X#  keeps track of how many lines it has read by incrementing
  310. X#  line_number.  The parser itself automatically initializes this
  311. X#  (global) variable to 0, so all you'll normally have to do is
  312. X#  increment it by one for each newline the tokenizer encounters.  If
  313. X#  you leave line_number alone, the parser simply does not print out
  314. X#  line numbers for errors it reports (which is somewhat less helpful,
  315. X#  but workable nonetheless).
  316. X#
  317. X#
  318. X#  V. Epsilon
  319. X#
  320. X#      If needed, epsilon (i.e. the empty string) can be specified by
  321. X#  an empty Icon string (e.g. rule REL(""|"that")).  "" is a perfectly
  322. X#  legal token.  It is absolutely vital, however, to note that
  323. X#  *epsilon is never pushed onto the value stack*.  Hence in the above
  324. X#  instance, if arg1 were to be accessed, an error would result if a
  325. X#  reduction via REL -> epsilon (rather than by REL -> "that")
  326. X#  occurred!  For this reason it is wise to keep epsilon isolated
  327. X#  (e.g. rule 10 REL("") and rule 11 REL("that"); note that giving
  328. X#  both rules an associativity probably isn't necessary here, although
  329. X#  giving the second rule the higher priority often *will* be).  Note
  330. X#  also that the tokenizer must never return an empty string as a
  331. X#  token (i.e. TOK("")).  Not only will the parser refuse to place it
  332. X#  on the value stack, but it will not even recognize it as a valid
  333. X#  lookahead symbol (since, by definition, it doesn't contain
  334. X#  anything).
  335. X#
  336. X#
  337. X#  VI. Reserved tokens ($ and error)
  338. X#
  339. X#      Besides epsilon, the only string (qua symbol) the tokenizer
  340. X#  cannot return at will is a TOK with either "error" or "$" as its
  341. X#  first field (e.g. TOK("$")).  The $ symbol denotes end-of-input,
  342. X#  and *must* be returned by the tokenizer when the input stream ends
  343. X#  (or when, for some other reason, the token stream has stopped).  I
  344. X#  could have cut the code so that failure or &null signalled the same
  345. X#  thing, but I prefer to use failure of the tokenizer to signal an
  346. X#  unexpected end of input, and I don't like the inconsistency of
  347. X#  having &null be a valid token.  The bottom line is that the
  348. X#  tokenizer must return TOK("$") on end-of-input.  You'll get used to
  349. X#  it :-).
  350. X#
  351. X#     The "error" terminal is reserved for error productions.  If a
  352. X#  syntax error occurs, and the token "error" exists in the grammar,
  353. X#  the parser will attempt to back up as one state to see if any of
  354. X#  them permits "error" as a valid lookahead symbol.  If so, the
  355. X#  parser jumps to the state that would have resulted if the parser
  356. X#  had encountered "error" at that point.  What this does is allow us
  357. X#  to write error-recovery rules:
  358. X#
  359. X#      global error_count
  360. X#      rule EXPR("(", EXPR, ")")
  361. X#          return arg1
  362. X#      end
  363. X#      rule REGEXP("(", REGEXP, "error")
  364. X#          write(&errout, "unmatched \"(\"; resynchronizing")
  365. X#          # Pretend we got a right parenthesis, and continue
  366. X#          # processing.
  367. X#          return arg1
  368. X#      end
  369. X#
  370. X#  So what happens if we get an expression without an enclosing right
  371. X#  parenthesis?  We get an error message, an increment of the error
  372. X#  count, and a resynchronized parser that's ready to go (and possibly
  373. X#  find more errors).  This mechanism is very much like the mechanism
  374. X#  YACC uses.
  375. X#
  376. X#      Note that IBPAG will only pop one state off of the stack in
  377. X#  efforts to find a state for which "error" is a legitimate lookahead
  378. X#  token; any more than this, and we could easily back so far out the
  379. X#  current production that we would end up causing confusion about
  380. X#  just where the error occurred!  If you write error productions,
  381. X#  make sure that the error terminal is at most one token removed from
  382. X#  the spot where you expect errors to occur.
  383. X#
  384. X#  
  385. X#  VII. IBPAG-Icon code compatibility (plus gotchas)
  386. X#
  387. X#      Regular Icon procedures may be freely intermingled with IBPAG
  388. X#  rules, so you can include iparse_tokens() right in the same file as
  389. X#  your rule definitions.  Note that rules, other than the header, in
  390. X#  fact, behave somewhat like procedures in the sense that when a rule
  391. X#  is triggered, the parser executes the code for that rule just the
  392. X#  way code for a procedure is executed when it is invoked.  The main
  393. X#  difference between Icon procedures and rules is that the rules can
  394. X#  only return one value or fail.  If you want a rule to be able to
  395. X#  produce more than one value, then have it return a coexpression.
  396. X#  Failure in a rule signals that the entire RHS of the production
  397. X#  should be discarded, and the stack should be returned to the state
  398. X#  it was in before that RHS's first symbol was shifted onto the
  399. X#  stack.  Failure is really just another way of providing the user
  400. X#  more control over error handling and recovery.  If you have nothing
  401. X#  useful to return, but do *not* want to signal an error, BE SURE TO
  402. X#  RETURN A NULL DESCRIPTOR ("return &null")!
  403. X#
  404. X#
  405. X#  VII. The parser itself
  406. X#
  407. X#      In the output file, the parser that IBPAG constructs is called
  408. X#  iparse().  You should invoke it somewhere to get the parser
  409. X#  started.  It takes a single argument: A stream (i.e. an open file).
  410. X#  You could pass it a string, theoretically, instead of a file.
  411. X#  Iparse() passes its first argument to iparse_tokens(), so be sure
  412. X#  iparse_tokens() understands that it is working on a string instead
  413. X#  of a file if you choose to do things this way.  IBPAG's output is
  414. X#  compressed somewhat, but the parser and error handler are human-
  415. X#  readable.  Please look at them and modify them as needed.  Note
  416. X#  especially the debugging facilities that are included with them
  417. X#  (e.g. dump_lists()).
  418. X#
  419. X#
  420. X#  VIII. Miscellaneous
  421. X#
  422. X#      As it encounters errors, the parser, iparse(), increments the
  423. X#  global variable errors by one.  Normally one botched production
  424. X#  will cause a series of cascading errors.  As a result, the error
  425. X#  handler only increments "errors" for the first in any unbroken
  426. X#  series of errors.  When (and if) the parser manages to
  427. X#  resynchronize itself, and the cascade terminates, the errors
  428. X#  variable is once again incremented normally, once for each error
  429. X#  encountered.
  430. X#
  431. X#      If no error processing is desired at all, you may set the
  432. X#  global variable fail_on_error to something other than &null.  This
  433. X#  tells iparser's error handler simply to fail if there are any
  434. X#  syntax errors.  Note that "error" productions (described above, in
  435. X#  the section on reserved tokens) will still work if they have been
  436. X#  used in the grammar.  So in fact, setting fail_on_errors will cause
  437. X#  failure to occur for all errors that cannot be handled by
  438. X#  user-defined error productions.  What this does is bypass the
  439. X#  built-in error detection and resynchronization routines.  This may
  440. X#  be useful if an IBPAG file is part of a much larger Icon program
  441. X#  that needs to have errors registered internally, and not spit out
  442. X#  separately to stderr by a renegade parser :-).
  443. X#
  444. X############################################################################
  445. X#
  446. X#     Below is a sample IBPAG source file.  It's kind of a hack, but
  447. X#  I'm sure it will adequately illustrate the basic principles:
  448. X#
  449. X#  link radcon
  450. X#  global ID_tbl
  451. X#  
  452. X#  procedure main()
  453. X#      iparse(&input)
  454. X#  end
  455. X#  
  456. X#  rule 0 left S(stream)
  457. X#      return
  458. X#  end
  459. X#  
  460. X#  rule 1 left stream(calc, stream)
  461. X#      return
  462. X#  end
  463. X#  
  464. X#  rule 1 left stream(calc)
  465. X#      return
  466. X#  end
  467. X#  
  468. X#  rule 2 none calc("CR")
  469. X#      return
  470. X#  end
  471. X#  
  472. X#  rule 3 left calc(expr, "CR")
  473. X#      # Radcon can't handle reals, so this doesn't really work.
  474. X#      line_number +:= 1
  475. X#      return write(radcon(arg1, \(\ID_tbl)["ibase"] | 10,
  476. X#                                \(\ID_tbl)["obase"] | 10))
  477. X#  end
  478. X#  
  479. X#  rule 4 none expr("ID")
  480. X#      if not (return \ID_tbl[arg1])
  481. X#      then write(&errout, "uninitialized variable, line ", line_number)
  482. X#      fail
  483. X#  end
  484. X#  
  485. X#  rule 6 right expr("ID", "=", expr)
  486. X#      initial ID_tbl := table()
  487. X#      ID_tbl[arg1] := arg3
  488. X#      return arg3
  489. X#  end
  490. X#  
  491. X#  rule 10 left expr(expr, "+", expr)
  492. X#      return arg1 + arg3
  493. X#  end
  494. X#  
  495. X#  rule 10 left expr(expr, "-", expr)
  496. X#      return arg1 - arg3
  497. X#  end
  498. X#  
  499. X#  rule 11 left expr(expr, "*", expr)
  500. X#      return arg1 * arg3
  501. X#  end
  502. X#  
  503. X#  rule 11 left expr(expr, "/", expr)
  504. X#      return arg1 / arg3
  505. X#  end
  506. X#  
  507. X#  rule 11 left expr(expr, "%", expr)
  508. X#      return arg1 % arg3
  509. X#  end
  510. X#  
  511. X#  rule 12 left expr(expr, "^", expr)
  512. X#      return arg1 ^ arg3
  513. X#  end
  514. X#  
  515. X#  rule 50 none expr("(", expr, ")")
  516. X#      return arg2
  517. X#  end
  518. X#  
  519. X#  rule 75 right expr("-", expr)
  520. X#      return -arg2
  521. X#  end
  522. X#  
  523. X#  rule 75 right expr("+", expr)
  524. X#      return +arg2
  525. X#  end
  526. X#  
  527. X#  rule 100 none expr("NUM")
  528. X#      if find(".", arg1)
  529. X#      then return real(arg1)
  530. X#      else return integer(arg1)
  531. X#  end
  532. X#  
  533. X#  #
  534. X#  # iparse_tokens:  file  -> TOK records (a generator)
  535. X#  #                 stream -> tokens
  536. X#  #
  537. X#  #     Where file is an open input stream, and tokens are TOK
  538. X#  #     records holding both the token type and actual token text.
  539. X#  #
  540. X#  #     TOK records contain two parts, a preterminal symbol (the first
  541. X#  #     "sym" field), and the actual text of the token ("str").  The
  542. X#  #     parser above only pays attention to the sym field, although the
  543. X#  #     user might well want to use the actual text.
  544. X#  #
  545. X#  procedure iparse_tokens(stream)
  546. X#  
  547. X#      local token, c, whitespace, operators
  548. X#      #global line_number
  549. X#      whitespace := '\r\t \n'
  550. X#      operators  := '+-*/^()='
  551. X#  
  552. X#      token := ""
  553. X#      every c := !(!&input || "\n") do {
  554. X#      if not any(whitespace ++ operators, c) then {
  555. X#          token ||:= c
  556. X#      } else {
  557. X#          if integer(token)
  558. X#          then suspend TOK("NUM", "" ~== token)
  559. X#          else suspend TOK("ID", "" ~== token)
  560. X#          if any(operators, c) then
  561. X#          suspend TOK(c)
  562. X#          else {
  563. X#          if c == "\n" then {
  564. X#              line_number +:= 1
  565. X#              suspend TOK("CR"|"CR")
  566. X#          }
  567. X#          }
  568. X#          token := ""
  569. X#      }
  570. X#      }
  571. X#     if integer(token)
  572. X#     then suspend TOK("NUM", "" ~== token)
  573. X#     else suspend TOK("ID",  "" ~== token)
  574. X#     suspend TOK("CR"|"$")
  575. X#
  576. X#  end
  577. X#
  578. X############################################################################
  579. X#
  580. X#  Links: options.icn
  581. X#
  582. X#  See also: maketbls.icn, preproc.icn
  583. X#
  584. X############################################################################
  585. X
  586. Xlink options, structs, ximage
  587. X
  588. Xglobal DEBUG, VERBOSE
  589. X# For other record declarations, see maketbls.icn.
  590. Xrecord RE(state, procname, sym, size)
  591. Xrecord SH(state)
  592. Xrecord AC()
  593. X
  594. Xprocedure main(a)
  595. X
  596. X    local f, f2, lname, opttbl, usage, tlst, sym, seen,
  597. X    act, elt, size, r, i, k
  598. X    # global ptbl, alst, glst
  599. X
  600. X    usage  := "usage:  ibpag [-d] [-v] [-f input-file]"
  601. X
  602. X    opttbl   := options(a, "dvf:") | stop(usage)
  603. X    if DEBUG := \opttbl["d"]
  604. X    then VERBOSE := 1
  605. X    else VERBOSE := opttbl["v"]
  606. X    f        := \opttbl["f"] | &input
  607. X    f2       := &output
  608. X
  609. X    ptbl := table()
  610. X    makeptbl(f, f2)
  611. X
  612. X    # set up (global) alst and glst (the action and goto lists)
  613. X    CONST_TABLE()
  614. X
  615. X    #
  616. X    # Set up tlst (a set of all tokens used in the grammar), then
  617. X    # turn the ACT records into RE, SH, or AC records.
  618. X    #
  619. X    tlst := set()
  620. X    # squeeze a list of rules out of the action list; find the
  621. X    # terminals in their right-hand sides
  622. X    every sym := !\(\(!(!alst)).by_rule).RHS do
  623. X    \sym.terminal & insert(tlst, sym.str)
  624. X    insert(tlst, "$")
  625. X
  626. X    #
  627. X    # Clobber all of the ACT records, and turn them into RE, SH, or AC
  628. X    # records.  Get rid of structurally identical, structures by
  629. X    # making them all point to the same structure.
  630. X    #
  631. X    if \VERBOSE then
  632. X    write(&errout, "Merging duplicate structures in action list...")
  633. X    seen := set()
  634. X    every i := 1 to *alst do {
  635. X        if /alst[i] then next
  636. X        if *alst[i] = 0 then {
  637. X        alst[i] := &null
  638. X        next
  639. X    }
  640. X        every k := key(alst[i]) do {
  641. X        if alst[i][k].str == "reduce" then {
  642. X        r := alst[i][k].by_rule
  643. X        size := *r.RHS
  644. X        # Don't count epsilon moves when calculating sizes!
  645. X        every elt := !r.RHS do
  646. X            if elt.str == "" & \elt.terminal then size -:= 1
  647. X        act := RE(alst[i][k].state, r.procname, r.LHS, size)
  648. X        }
  649. X        # Don't need to store all this info for shifts or accepts.
  650. X        else if alst[i][k].str == "shift"
  651. X             then act := SH(alst[i][k].state)
  652. X             else act := AC()
  653. X        if alst[i][k] := equiv(act, !seen)
  654. X        then next
  655. X        else {
  656. X        alst[i][k] := act
  657. X        insert(seen, act)
  658. X        }
  659. X    }
  660. X        # Simple optimization; if alst[i] is structured identically to
  661. X    # a previously seen table, use the previously seen one!  Note
  662. X    # that equiv is more general than Equiv (it can accept sets
  663. X    # and tables).  See the IPL for equiv().  Equiv() is in
  664. X    # debug.src. 
  665. X        if alst[i] := equiv(alst[i], !seen)
  666. X        then next else insert(seen, alst[i])
  667. X    }
  668. X
  669. X    write(f2, "")
  670. X    write(f2, "############################################################################")
  671. X    write(f2, "#")
  672. X    write(f2, "#      The following code has been inserted by IBPAG (Icon-based")
  673. X    write(f2, "#  Parser Generator).  It contains a stack-based LR parser, an")
  674. X    write(f2, "#  error handler, and a debugging tool.  The user must supply a")
  675. X    write(f2, "#  tokenizing routine called iparse_tokens().")
  676. X    write(f2, "#")
  677. X    write(f2, "#      For a description of IBPAG source-file format, see the")
  678. X    write(f2, "#  documentation prepended to ibpag.icn.")
  679. X    write(f2, "#")
  680. X    write(f2, "############################################################################")
  681. X    write(f2, "#")
  682. X    write(f2, "#  See also: ibpag.icn, maketbls.icn, preproc.icn, itokens.icn")
  683. X    write(f2, "#")
  684. X    write(f2, "############################################################################")
  685. X    write(f2, "")
  686. X    write(f2, "# uncomment for the compiler")
  687. X    write(f2, "# invocable \"symbol\", \"TOK\", \"RE\", \"SH\", \"AC\"")
  688. X    write(f2, "")
  689. X    write(f2, "# I use ximage for debugging; remove it if you don't need it.")
  690. X    write(f2, "link codeobj#, ximage")
  691. X    write(f2, "")
  692. X    write(f2, "record symbol(str, terminal)")
  693. X    write(f2, "record TOK(sym, str)")
  694. X    write(f2, "record RE(state, procname, sym, size)")
  695. X    write(f2, "record SH(state)")
  696. X    write(f2, "record AC()")
  697. X    write(f2, "")
  698. X    write(f2, "global line_number, errors, fail_on_error")
  699. X    write(f2, "")
  700. X    write(f2, "#")
  701. X    write(f2, "# iparse:  file     -> ?")
  702. X    write(f2, "#          stream   -> ?")
  703. X    write(f2, "#")
  704. X    write(f2, "#     Where stream is an open file, and ? represents the user-defined")
  705. X    write(f2, "#     result of a completed parse of file, from the current location")
  706. X    write(f2, "#     up to the point where the parser executes an \"accept\" action.")
  707. X    write(f2, "#")
  708. X    write(f2, "#     The second to fifth arguments are used on recursive calls from")
  709. X    write(f2, "#     the error handler, iparse_error.  Ignore them, unless you are")
  710. X    write(f2, "#     sure of what you are doing!")
  711. X    write(f2, "#")
  712. X    write(f2, "procedure iparse(stream, state_stack, value_stack, next_token, err_state)")
  713. X    write(f2, "")
  714. X    write(f2, "    local start_symbol, token, act, last_symbol, arglist")
  715. X    write(f2, "    static alst, glst")
  716. X    write(f2, "    #global line_number, errors")
  717. X    write(f2, "    initial {")
  718. X    every lname := "alst" |"glst" do {
  719. X    encode(variable(lname)) ? {
  720. X        writes(f2, "\t", lname, " := decode(\"")
  721. X        if write(f2, move(47), "_") then {
  722. X        while write(f2, "\t    ",move(60), "_")
  723. X        write(f2, "\t    ", tab(0), "\")")
  724. X        }
  725. X        else write(f2, tab(0), "\")")
  726. X    }
  727. X    }
  728. X    write(f2, "\t#")
  729. X    write(f2, "\t# Uncomment the following if you want a look at the state and goto")
  730. X    write(f2, "\t# tables.  If you aren't planning on looking at them, find the")
  731. X    write(f2, "\t# procedure definition for dump_lists below, and delete it.  Why")
  732. X    write(f2, "\t# keep it around if it's not being used?")
  733. X    write(f2, "\t#")
  734. X    write(f2, "\t# dump_lists(&errout, alst, glst)")
  735. X    write(f2, "    }")
  736. X    write(f2, "    #")
  737. X    write(f2, "    # New, not recursive, invocation; reset stacks, line number and")
  738. X    write(f2, "    # error count.")
  739. X    write(f2, "    #")
  740. X    write(f2, "    start_symbol := ", image(start_symbol))
  741. X    write(f2, "    /err_state   := 0")
  742. X    write(f2, "    /state_stack := [1] & line_number := 0 & errors := 0")
  743. X    write(f2, "    /value_stack := []")
  744. X    write(f2, "    /next_token  := create iparse_tokens(stream)")
  745. X    write(f2, "")
  746. X    write(f2, "    # &trace := -1")
  747. X    write(f2, "")
  748. X    write(f2, "    while token := @next_token do {")
  749. X    write(f2, "\trepeat {")
  750. X    write(f2, "\t    act := \\alst[state_stack[1]][token.sym] | {")
  751. X    write(f2, "\t\t#")
  752. X    write(f2, "\t\t# You can replace this error handler with whatever you")
  753. X    write(f2, "\t\t# like, and have it do whatever you like.")
  754. X    write(f2, "\t\t#")
  755. X    write(f2, "\t\treturn iparse_error(alst, state_stack, value_stack,")
  756. X    write(f2, "\t\t\t\t    token, next_token, err_state + 1)")
  757. X    write(f2, "\t    }")
  758. X    write(f2, "")
  759. X    write(f2, "\t    # If we're not working on an error production, assume that")
  760. X    write(f2, "\t    # we have a fully (re)synchronized parser, & clear err_state. ")
  761. X    write(f2, "\t    #")
  762. X    write(f2, "\t    (\\last_symbol | token.sym) == \"error\" | { err_state := 0 }")
  763. X    write(f2, "\t    last_symbol := token.sym")
  764. X    write(f2, "")
  765. X    write(f2, "\t    case type(act) of {")
  766. X    write(f2, "\t        \"SH\"   :  {")
  767. X    write(f2, "\t\t    # push the next state onto the state stack")
  768. X    write(f2, "\t    \t    push(state_stack, act.state)")
  769. X    write(f2, "\t\t    # push the current token's literal value onto the")
  770. X    write(f2, "\t\t    # value stack")
  771. X    write(f2, "\t\t    push(value_stack, token.str)")
  772. X    write(f2, "\t\t    # break out of enclosing repeat loop")
  773. X    write(f2, "\t\t    break")
  774. X    write(f2, "\t        }")
  775. X    write(f2, "\t        \"RE\"  :  {")
  776. X    write(f2, "\t    \t    arglist := []")
  777. X    write(f2, "\t\t    #")
  778. X    write(f2, "\t\t    # Pop as many elements off of the token stack as")
  779. X    write(f2, "\t\t    # there are symbols in the right-hand side of the")
  780. X    write(f2, "\t\t    # rule.  Push these elements onto an argument list.")
  781. X    write(f2, "\t\t    #")
  782. X    write(f2, "\t\t    every 1 to act.size do {")
  783. X    write(f2, "\t\t        pop(state_stack)")
  784. X    write(f2, "\t\t        push(arglist, pop(value_stack))")
  785. X    write(f2, "\t\t    }")
  786. X    write(f2, "\t\t    #")
  787. X    write(f2, "\t\t    # Check to goto list to see what state we should")
  788. SHAR_EOF
  789. true || echo 'restore of ibpag.icn failed'
  790. fi
  791. echo 'End of  part 1'
  792. echo 'File ibpag.icn is continued in part 2'
  793. echo 2 > _shar_seq_.tmp
  794. exit 0
  795. -- 
  796.  
  797.    -Richard L. Goerwitz              goer%midway@uchicago.bitnet
  798.    goer@midway.uchicago.edu          rutgers!oddjob!ellis!goer
  799.